﻿# copyright 2025, darkathena@qq.com
# All rights reserved.
# This script is used to extract the centralized *_Om_*.tar.gz *_Server_*.tar.gz *_Symbol*.tar.gz *Kernel*.bin files to the same directory as the original tar package
# 
# GaussDB Universal Extract Script (PowerShell Version)
# Usage: .\uncompress_gaussdb.ps1 <tar_package_path> <CPU_architecture>
# Architecture support: x86_64/x86/arm/arm64/aarch/aarch64 (case insensitive, mapped to x86_64 and aarch64)
# Function: Extract centralized *_Om_*.tar.gz *_Server_*.tar.gz *_Symbol_*.tar.gz *Kernel*.bin files to the same directory as the original tar package
# 

param(
    [Parameter(Mandatory=$true)]
    [string]$TarFile,
    
    [Parameter(Mandatory=$true)]
    [string]$ArchInput
)

# Set error handling
$ErrorActionPreference = "Stop"

# Global variables: tar command path (set by Test-RequiredTools function)
$script:TarPath = $null
$script:SevenZipPath = $null
$script:WorkDirFullPath = $null
$script:UsingShortPath = $false

# Colored output functions
function Write-Info {
    param([string]$Message)
    Write-Host "[INFO] $Message" -ForegroundColor Green
}

function Write-Warn {
    param([string]$Message)
    Write-Host "[WARN] $Message" -ForegroundColor Yellow
}

function Write-Error {
    param([string]$Message)
    Write-Host "[ERROR] $Message" -ForegroundColor Red
}

function Write-Step {
    param([string]$Message)
    Write-Host "`n=== $Message ===" -ForegroundColor Cyan
}

# Check and handle long path issue
function Test-And-HandleLongPath {
    param(
        [string]$CurrentLocation = (Get-Location).Path,
        [string]$StepName = ""
    )
    
    if ($CurrentLocation.Length -gt 150) {
        Write-Warn "Long path detected in $StepName. Path length: $($CurrentLocation.Length)"
        Write-Warn "Switching to short path for remaining operations."
        
        if (-not $script:UsingShortPath) {
            # First time switching to short path
            $script:UsingShortPath = $true
            
            # Switch to root work directory
            Set-Location $script:WorkDirFullPath
            
            # Copy current content to work directory
            $TempDir = "temp_extract_$(Get-Random)"
            Copy-Item $CurrentLocation $TempDir -Recurse -Force
            Set-Location $TempDir
            
            Write-Info "Working in short path: $(Get-Location)"
            return $true
        } else {
            # Already using short path, just copy content
            $ParentDir = Split-Path $CurrentLocation -Parent
            $TempDir = "temp_$(Get-Random)"
            Set-Location $script:WorkDirFullPath
            Copy-Item $CurrentLocation $TempDir -Recurse -Force
            Set-Location $TempDir
            
            Write-Info "Switched to shorter path: $(Get-Location)"
            return $true
        }
    }
    
    return $false
}

# Find tar.exe path
function Find-TarPath {
    # First try tar command in PATH
    try {
        $output = & tar --version 2>&1
        if ($output -match "tar") {
            return "tar"
        }
    }
    catch {
        # Not found in PATH, try common installation paths
    }
    
    # Common tar installation paths (Git for Windows, etc.)
    $commonPaths = @(
        "${env:ProgramFiles}\Git\usr\bin\tar.exe",
        "${env:ProgramFiles(x86)}\Git\usr\bin\tar.exe",
        "${env:ProgramData}\chocolatey\lib\git\tools\usr\bin\tar.exe"
    )
    
    foreach ($path in $commonPaths) {
        if (Test-Path $path) {
            try {
                $output = & $path --version 2>&1
                if ($output -match "tar") {
                    return $path
                }
            }
            catch {
                continue
            }
        }
    }
    
    return $null
}

# Find 7z.exe path
function Find-7ZipPath {
    # First try 7z command in PATH
    try {
        $output = & 7z 2>&1
        if ($output -match "7-Zip") {
            return "7z"
        }
    }
    catch {
        # Not found in PATH, try common installation paths
    }
    
    # Common 7-Zip installation paths
    $commonPaths = @(
        "${env:ProgramFiles}\7-Zip\7z.exe",
        "${env:ProgramFiles(x86)}\7-Zip\7z.exe",
        "${env:ProgramData}\chocolatey\lib\7zip\tools\7z.exe",
        "${env:ProgramData}\chocolatey\bin\7z.exe"
    )
    
    foreach ($path in $commonPaths) {
        if (Test-Path $path) {
            try {
                $output = & $path 2>&1
                if ($output -match "7-Zip") {
                    return $path
                }
            }
            catch {
                continue
            }
        }
    }
    
    return $null
}

# Check required tools
function Test-RequiredTools {
    Write-Step "Checking Required Tools"
    
    # Check tar command
    $script:TarPath = Find-TarPath
    if ($script:TarPath) {
        Write-Info "Found tool: tar (path: $script:TarPath)"
    }
    else {
        Write-Error "Missing required tool: tar"
        Write-Error ""
        Write-Error "Installation methods:"
        Write-Error "1. Install Git for Windows (recommended): https://git-scm.com/"
        Write-Error "2. Ensure Windows 10/11 version is sufficient (built-in tar)"
        Write-Error "3. If Git is installed, ensure tar.exe is in one of these paths:"
        Write-Error "   - ${env:ProgramFiles}\Git\usr\bin\tar.exe"
        Write-Error "   - ${env:ProgramFiles(x86)}\Git\usr\bin\tar.exe"
        exit 1
    }
    
    # Check 7z command (required for RPM processing)
    $script:SevenZipPath = Find-7ZipPath
    if ($script:SevenZipPath) {
        Write-Info "Found tool: 7z (path: $script:SevenZipPath)"
    }
    else {
        Write-Error "Missing required tool: 7z"
        Write-Error "7-Zip is required for processing RPM files"
        Write-Error ""
        Write-Error "Installation methods:"
        Write-Error "1. Chocolatey: choco install 7zip"
        Write-Error "2. Official download: https://www.7-zip.org/"
        Write-Error "3. If already installed, ensure 7z.exe is in one of these paths:"
        Write-Error "   - ${env:ProgramFiles}\7-Zip\7z.exe"
        Write-Error "   - ${env:ProgramFiles(x86)}\7-Zip\7z.exe"
        Write-Error "4. Or manually add 7-Zip directory to PATH environment variable"
        exit 1
    }
}

# Check parameters
if (-not $TarFile -or -not $ArchInput) {
    Write-Error "Parameter error!"
    Write-Host "Usage: .\uncompress_gaussdb.ps1 <tar_package_path> <CPU_architecture>"
    Write-Host "Architecture support: x86_64/x86/arm/arm64/aarch/aarch64 (case insensitive)"
    Write-Host "Example: .\uncompress_gaussdb.ps1 DBS-GaussDB-Kylin-Kernel_xxx.tar.gz x86_64"
    Write-Host "Example: .\uncompress_gaussdb.ps1 DBS-GaussDB-Kylin-Kernel_xxx.tar.gz arm64"
    exit 1
}

# Check if tar package exists
if (-not (Test-Path $TarFile)) {
    Write-Error "Tar package file does not exist: $TarFile"
    exit 1
}

# Get absolute path and directory of tar package
$TarFullPath = (Resolve-Path $TarFile).Path
$TarDir = Split-Path $TarFullPath -Parent
$TarBasename = [System.IO.Path]::GetFileNameWithoutExtension($TarFile)

Write-Info "Original tar package: $TarFullPath"
Write-Info "Working directory: $TarDir"

# Architecture mapping (case insensitive)
$ArchLower = $ArchInput.ToLower()
switch ($ArchLower) {
    {$_ -in @("x86_64", "x86")} {
        $TargetArch = "x86_64"
        $ArchPattern = "x86_64|X86"
        break
    }
    {$_ -in @("arm64", "arm", "aarch64", "aarch")} {
        $TargetArch = "aarch64"
        $ArchPattern = "aarch64|arm64"
        break
    }
    default {
        Write-Error "Unsupported architecture: $ArchInput"
        Write-Error "Supported architectures: x86_64/x86/arm/arm64/aarch/aarch64"
        exit 1
    }
}

Write-Info "Target architecture: $TargetArch"

# Check required tools
Test-RequiredTools

# Enter tar package directory
Set-Location $TarDir

# Create temporary working directory
$WorkDir = "gaussdb_extract_$((Get-Date).Ticks)"
New-Item -ItemType Directory -Path $WorkDir -Force | Out-Null
$script:WorkDirFullPath = Join-Path $TarDir $WorkDir
Set-Location $WorkDir

try {
    # Step 1: Extract main tar
    Write-Step "Step 1: Extracting main tar"
    & $script:TarPath -xf $TarFullPath
    
    # Check path length after main tar extraction
    $CurrentPath = (Get-Location).Path
    Test-And-HandleLongPath -CurrentLocation $CurrentPath -StepName "Step 1"
    
    # Find extracted directory
    $MainDir = Get-ChildItem -Directory | Where-Object { $_.Name -like "*GaussDB*" } | Select-Object -First 1
    if (-not $MainDir) {
        throw "Cannot find GaussDB main directory"
    }
    
    Write-Info "Entering directory: $($MainDir.Name)"
    Set-Location "$($MainDir.Name)\packages"
    
    # Check path length after entering packages directory
    $CurrentPath = (Get-Location).Path
    Test-And-HandleLongPath -CurrentLocation $CurrentPath -StepName "Step 1 (packages)"
    
    # Step 2: Extract _all.tar.gz file
    Write-Step "Step 2: Extracting _all.tar.gz file"
    $AllTar = Get-ChildItem -File "*_all.tar.gz" | Select-Object -First 1
    if (-not $AllTar) {
        throw "Cannot find *_all.tar.gz file"
    }
    
    Write-Info "Extracting file: $($AllTar.Name)"
    & $script:TarPath -xf $AllTar.Name
    Remove-Item $AllTar.Name -Force
    
    # Find extracted directory
    $AllDir = Get-ChildItem -Directory "*_all" | Select-Object -First 1
    if (-not $AllDir) {
        throw "Cannot find *_all directory"
    }
    
    Set-Location $AllDir.Name
    
    # Check path length after entering _all directory
    $CurrentPath = (Get-Location).Path
    Test-And-HandleLongPath -CurrentLocation $CurrentPath -StepName "Step 2"
    
    # Step 3: Extract _noarch.tar.gz file
    Write-Step "Step 3: Extracting _noarch.tar.gz file"
    $NoarchTar = Get-ChildItem -File "*_noarch.tar.gz" | Select-Object -First 1
    if (-not $NoarchTar) {
        throw "Cannot find *_noarch.tar.gz file"
    }
    
    Write-Info "Extracting file: $($NoarchTar.Name)"
    & $script:TarPath -xf $NoarchTar.Name
    Remove-Item $NoarchTar.Name -Force
    
    # Find noarch directory
    $NoarchDir = Get-ChildItem -Directory "*_noarch" | Select-Object -First 1
    if (-not $NoarchDir) {
        throw "Cannot find *_noarch directory"
    }
    
    Set-Location "$($NoarchDir.Name)\repo"
    
    # Check path length after entering noarch/repo directory
    $CurrentPath = (Get-Location).Path
    Test-And-HandleLongPath -CurrentLocation $CurrentPath -StepName "Step 3"
    
    # Step 4: Extract RPM file
    Write-Step "Step 4: Extracting RPM file"
    $RpmFile = Get-ChildItem -File "*.noarch.rpm" | Select-Object -First 1
    if (-not $RpmFile) {
        throw "Cannot find *.noarch.rpm file"
    }
    
    Write-Info "Extracting RPM file: $($RpmFile.Name)"
    
    # Try using 7z to extract RPM (recommended method)
    $rpmExtracted = $false
    try {
        Write-Info "Using 7z to extract RPM: $($script:SevenZipPath)"
        & $script:SevenZipPath x $RpmFile.Name -y > $null 2>&1
        # 7z extracting RPM produces cpio file, need further extraction
        $CpioFile = Get-ChildItem -File "*.cpio" | Select-Object -First 1
        if ($CpioFile) {
            Write-Info "Extracting CPIO file: $($CpioFile.Name)"
            & $script:SevenZipPath x $CpioFile.Name -y > $null 2>&1
            Remove-Item $CpioFile.Name -Force
            $rpmExtracted = $true
        }
    }
    catch {
        Write-Warn "7z extraction failed: $($_.Exception.Message)"
    }
    
    # If 7z fails, try other methods
    if (-not $rpmExtracted) {
        Write-Warn "Cannot extract RPM file, possible reasons:"
        Write-Warn "1. 7-Zip tool not installed"
        Write-Warn "2. RPM file format not supported"
        Write-Error "Recommend installing 7-Zip: choco install 7zip"
        throw "RPM file extraction failed"
    }
    
    Remove-Item $RpmFile.Name -Force
    
    Set-Location "opt\cloud\GaussDB-kernel"
    
    # Check path length after entering opt/cloud/GaussDB-kernel directory
    $CurrentPath = (Get-Location).Path
    Test-And-HandleLongPath -CurrentLocation $CurrentPath -StepName "Step 4"
    
    # Step 5: Extract architecture-related tar.gz files
    Write-Step "Step 5: Extracting architecture-related installation packages"
    
    # Find corresponding tar.gz files based on architecture
    $ArchTar = $null
    if ($TargetArch -eq "x86_64") {
        $ArchTar = Get-ChildItem -File "*ha-x86_64*.tar.gz" | Select-Object -First 1
    } else {
        $ArchTar = Get-ChildItem -File "*ha-aarch64*.tar.gz" | Select-Object -First 1
    }
    
    if (-not $ArchTar) {
        throw "Cannot find tar.gz file for architecture ($TargetArch)"
    }
    
    Write-Info "Extracting architecture file: $($ArchTar.Name)"
    
    # Check path length before architecture file extraction
    $CurrentPath = (Get-Location).Path
    Test-And-HandleLongPath -CurrentLocation $CurrentPath -StepName "Step 5"
    
    & $script:TarPath -xf $ArchTar.Name
    Get-ChildItem -File "*install*.tar.gz" | Remove-Item -Force
    
    # Find extracted directory
    $ArchDir = Get-ChildItem -Directory "*install*" | Select-Object -First 1
    if (-not $ArchDir) {
        throw "Cannot find install directory"
    }
    
    Set-Location $ArchDir.Name
    
    # Check path length after entering install directory
    $CurrentPath = (Get-Location).Path
    Test-And-HandleLongPath -CurrentLocation $CurrentPath -StepName "Step 5 (install)"
    
    # Step 6: Extract Server-related tar.gz files
    Write-Step "Step 6: Extracting Server-related files"
    
    # Find and extract all Server-related tar.gz files
    $ServerTarFiles = Get-ChildItem -File "*Server*.tar.gz"
    foreach ($file in $ServerTarFiles) {
        Write-Info "Extracting Server file: $($file.Name)"
        & $script:TarPath -xf $file.Name
        
        # Check path length after each Server file extraction
        $CurrentPath = (Get-Location).Path
        Test-And-HandleLongPath -CurrentLocation $CurrentPath -StepName "Step 6 (Server files)"
    }
    
    $BitTarFiles = Get-ChildItem -File "*64bit*.tar.gz"
    foreach ($file in $BitTarFiles) {
        Write-Info "Extracting 64bit file: $($file.Name)"
        & $script:TarPath -xf $file.Name
        
        # Check path length after each 64bit file extraction
        $CurrentPath = (Get-Location).Path
        Test-And-HandleLongPath -CurrentLocation $CurrentPath -StepName "Step 6 (64bit files)"
    }
    
    # Step 7: Find and copy target files
    Write-Step "Step 7: Finding and copying target files"
    
    $TargetFilesFound = 0
    
    # Find *_Om_*.tar.gz files
    $OmFiles = Get-ChildItem -Recurse -File "*_Om_*.tar.gz" -ErrorAction SilentlyContinue
    if ($OmFiles) {
        foreach ($file in $OmFiles) {
            Write-Info "Found _Om_ file: $($file.Name)"
            Copy-Item $file.FullName $TarDir
            $TargetFilesFound++
        }
    } else {
        Write-Warn "No _Om_*.tar.gz files found"
    }
    
    # Find *_Server_*.tar.gz files
    $ServerFiles = Get-ChildItem -Recurse -File "*_Server_*.tar.gz" -ErrorAction SilentlyContinue
    if ($ServerFiles) {
        foreach ($file in $ServerFiles) {
            Write-Info "Found _Server_ file: $($file.Name)"
            Copy-Item $file.FullName $TarDir
            $TargetFilesFound++
        }
    } else {
        Write-Warn "No _Server_*.tar.gz files found"
    }
    
    # Find *_Symbol_*.tar.gz files
    $SymbolFiles = Get-ChildItem -Recurse -File "*_Symbol*.tar.gz" -ErrorAction SilentlyContinue
    if ($SymbolFiles) {
        foreach ($file in $SymbolFiles) {
            Write-Info "Found _Symbol file: $($file.Name)"
            Copy-Item $file.FullName $TarDir
            $TargetFilesFound++
        }
    } else {
        Write-Warn "No _Symbol*.tar.gz files found"
    }
    
    # Find *Kernel*.bin files
    $KernelFiles = Get-ChildItem -Recurse -File "*Kernel*.bin", "*.bin" -ErrorAction SilentlyContinue
    if ($KernelFiles) {
        foreach ($file in $KernelFiles) {
            Write-Info "Found Kernel file: $($file.Name)"
            Copy-Item $file.FullName $TarDir
            $TargetFilesFound++
        }
    } else {
        Write-Warn "No Kernel*.bin files found"
    }
    
    # Summary
    Write-Step "Extraction Complete"
    Write-Info "Original tar package: $TarFile"
    Write-Info "Target architecture: $TargetArch"
    Write-Info "Extracted files: $TargetFilesFound"
    Write-Info "Output directory: $TarDir"
    
    if ($TargetFilesFound -eq 0) {
        Write-Warn "Warning: No target files found"
        exit 1
    }
    
    Write-Info "All files have been extracted to: $TarDir"
    Write-Info "Please check the following files:"
    
    # List extracted files
    $ExtractedFiles = @()
    $ExtractedFiles += Get-ChildItem "$TarDir\*_Om_*.tar.gz" -ErrorAction SilentlyContinue
    $ExtractedFiles += Get-ChildItem "$TarDir\*_Server_*.tar.gz" -ErrorAction SilentlyContinue
    $ExtractedFiles += Get-ChildItem "$TarDir\*_Symbol_*.tar.gz" -ErrorAction SilentlyContinue
    $ExtractedFiles += Get-ChildItem "$TarDir\*.bin" -ErrorAction SilentlyContinue
    
    if ($ExtractedFiles) {
        $ExtractedFiles | ForEach-Object { 
            Write-Host "  $($_.Name) ($([math]::Round($_.Length/1MB, 2)) MB)" -ForegroundColor White
        }
    }
}
catch {
    Write-Error "Execution failed: $($_.Exception.Message)"
    exit 1
}
finally {
    # Step 8: Clean up temporary directory
    Write-Step "Cleaning up temporary directory"
    Set-Location $TarDir
    if (Test-Path $WorkDir) {
        Remove-Item $WorkDir -Recurse -Force -ErrorAction SilentlyContinue
        Write-Info "Cleaned up temporary directory: $WorkDir"
    }
}

Write-Info "`n🎉 Task completed! 🎉" 

